#ifndef __FLOAT__
#define __FLOAT__

#include <iostream>
#include <string>
#include <cassert>

typedef char byte;

using namespace std;

class Float{

	public:

		/**
		 * Constructores
		 */
		Float();
		Float(const string&);

		bool operator== (const Float&);
		bool operator<= (const Float&) const;

		/**
		 * operadores aritmeticos
		 */
		friend Float operator+(const Float&, const Float&);
		friend Float operator-(const Float&, const Float&);
		friend Float operator*(const Float&, const Float&);
		void operator=(const Float&);

		string mostrar() const;

	private:
		static void sumaMismoSigno(const Float&, const Float&, Float&);
		static void sumaDistitnoSigno(const Float&, const Float&, Float&, bool);
		
		void cambiarSigno();

		long long int precision;
		// mantisa[0] es la parte mas significativa (big-indian)
		char* mantisa;
		long long int exponente;
		bool signo;

};


Float::Float(){
	precision = 0;
	mantisa = NULL;
	exponente = 0;
	signo = false;
}

/**
 * Requiere que el numero tenga una coma, tenga un digito entero(puede ser 0) y que tenga sentido o que sea el '0'
 */
Float::Float(const string& num) {
    cout << num << endl;
    if(num.size() == 1 && num[0] == '0') {
        signo = false;
        precision = 0;
        mantisa = new byte[1];
        exponente = 0;
        return;
    }
    //sino es cualquier otro numero
    int pos=0;
    if(num[pos] == '-') {
        signo=true;
        pos++;
    } else {
        signo = false;
    }
    //me qudo con lo entero en "entero"
    string entero = "";
    while(num[pos] == '0') {
        pos++;
    }
    while(num[pos] != ',') {
        entero += num[pos];
        pos++;
    }
    //me quedo con lo factal en "decimal"
    string decimal = "";
    pos = num.size()-1;
    while(num[pos] == '0')
        pos--;
    while(num[pos] != ',') {
        decimal = num[pos] + decimal;
        pos--;
    }
    //hasta aca esta en entero y el decimal por separados
    //se calcula exponente
    if(entero.size() > 0) {
        exponente = entero.size();
    } else {
        exponente=0;
        while(decimal[exponente] == '0')
            exponente++;
        decimal = decimal.substr(exponente);
        exponente=exponente*(-1);
    }
    //hasta aca ya esta el exponente
    string todo = entero+decimal;
    precision = todo.size();
    mantisa = new byte[precision];
    for(pos=0;pos<precision;pos++)
        mantisa[pos] = todo[pos]-'0';
}

Float operator+(const Float& f1, const Float& f2) {
    Float ret;
    if(f1.signo == f2.signo) {
        Float::sumaMismoSigno(f1,f2,ret);
    } else {
    	if(f2<=f1)
            Float::sumaDistitnoSigno(f1,f2,ret,false);
        else
            Float::sumaDistitnoSigno(f2,f1,ret,true);
    }
    return ret;
}

Float operator-(const Float& f1, const Float& f2) {
	Float aux = f2;
	aux.cambiarSigno();
	return f1+aux;
}

Float operator*(const Float& f1, const Float& f2) {
	Float ret;
	ret.signo = f1.signo != f2.signo;
	ret.exponente = f1.exponente + f2.exponente;
	Float aux1 = f1;
	Float aux2 = f2;
	if(aux1.precision < aux2.precision) {
		ret.precision = aux2.precision;
		byte *newMantisa = new byte[ret.precision];
		for(int i=0;i<aux1.precision;i++)
			newMantisa[i] = aux1.mantisa[i];
		for(int i=aux1.precision;i<aux2.precision;i++)
			newMantisa[i] = 0;
		delete [] aux1.mantisa;
		aux1.mantisa = newMantisa;
		aux1.precision = 2*aux2.precision;
	} else if(aux2.precision < aux1.precision) {
		ret.precision = aux1.precision;
		byte *newMantisa = new byte[ret.precision];
		for(int i=0;i<aux2.precision;i++)
			newMantisa[i] = aux2.mantisa[i];
		for(int i=aux2.precision;i<aux1.precision;i++)
			newMantisa[i] = 0;
		delete [] aux2.mantisa;
		aux2.mantisa = newMantisa;
		aux2.precision = 2*aux1.precision;
	} else {
		ret.precision = 2*aux1.precision;
	}
	byte *newMantisa = new byte[ret.precision];
	for(int i=0;i<ret.precision;i++)
		newMantisa[i] = 0;
	for(int i=aux1.precision-1;i>=0;i--) {
		for(int j=aux2.precision-1;j>=0;j--) {
			byte auxVal = (aux1.mantisa[j]*aux2.mantisa[i] + newMantisa[i+j+1]);
			newMantisa[i+j+1] = auxVal%10;
			newMantisa[i+j] += auxVal/10;
		}
	}
	cout << "RET.P: " <<ret.precision << endl;
	if(ret.mantisa != NULL) delete [] ret.mantisa;
	ret.mantisa = newMantisa;
	return ret;
}

void Float::sumaMismoSigno(const Float& f1, const Float& f2, Float& ret) {
    assert(f1.signo == f2.signo);
    ret.signo = f1.signo;
    if(f1.exponente == f2.exponente) {
        ret.exponente = f1.exponente;
        int minPrecision; //dese donde sumar
        if(f1.precision == f2.precision) {
            ret.precision = f1.precision;
            ret.mantisa = new byte[ret.precision];
            minPrecision = f1.precision;
        } else {
            if(f1.precision < f2.precision) {
                ret.precision = f2.precision;
                ret.mantisa = new byte[ret.precision];
                minPrecision = f1.precision;
                for(int i=minPrecision;i<f2.precision;i++)
                    ret.mantisa[i] = f2.mantisa[i];
            } else { //f2.presicion > f1.presicion
                ret.precision = f1.precision;
                ret.mantisa = new byte[ret.precision];
                minPrecision = f2.precision;
                for(int i=minPrecision;i<f1.precision;i++)
                    ret.mantisa[i] = f1.mantisa[i];
            }
        }
        bool hayCarry = false;
        for(int i=minPrecision-1;i>=0;i--) {
            byte aux = f1.mantisa[i] + f2.mantisa[i] + (hayCarry?1:0);
            hayCarry = aux>9;
            aux=aux%10;
            ret.mantisa[i] = aux;
        }
    } else {//los exponentes son diferentes
        if(f1.exponente < f2.exponente) {
            Float aux;
            aux = f1;
            long long int dif = f2.exponente - f1.exponente;
            long long int nuevaP = aux.precision + dif;
            aux.exponente = f2.exponente;
            byte *mantisaAux =  new byte[nuevaP];
            for(int i=0;i<dif;i++)
                mantisaAux[i] = 0;
            for(int i=dif;i<nuevaP;i++)
                mantisaAux[i] = aux.mantisa[i-dif];
            delete [] aux.mantisa;
            aux.mantisa = mantisaAux;
            aux.precision = nuevaP;
            aux.exponente = f2.exponente;
            Float::sumaMismoSigno(aux,f2,ret);
        } else {
            Float aux;
            aux = f2;
            long long int dif = f1.exponente - f2.exponente;
            long long int nuevaP = aux.precision + dif;
            aux.exponente = f1.exponente;
            byte *mantisaAux =  new byte[nuevaP];
            for(int i=0;i<dif;i++)
                mantisaAux[i] = 0;
            for(int i=dif;i<nuevaP;i++)
                mantisaAux[i] = aux.mantisa[i-dif];
            delete [] aux.mantisa;
            aux.mantisa = mantisaAux;
            aux.precision = nuevaP;
            aux.exponente = f1.exponente;
            Float::sumaMismoSigno(f1,aux,ret);
        }
    }
    if(ret.mantisa[0] < f1.mantisa[0] && ret.mantisa[0] < f2.mantisa[0]) {
        byte *newMantisa = new byte[ret.precision+1];
        for(int i=0;i<ret.precision;i++)
                    newMantisa[i+1] = ret.mantisa[i];
        newMantisa[0] = 1;
        delete [] ret.mantisa;
        ret.mantisa = newMantisa;
        ret.exponente = ret.exponente + 1;
        ret.precision = ret.precision+1;
        while(ret.mantisa[ret.precision-1] == 0) {
            ret.precision--;
        }
    }
    while(ret.mantisa[ret.precision-1] == 0 && ret.precision > 0 ){
            ret.precision--;
    }
}

//el mayor es el f1
void Float::sumaDistitnoSigno(const Float& f1, const Float& f2, Float& ret, bool cambiarSigno) {
    assert(f1.signo != f2.signo);
    if(f1.exponente == f2.exponente) {
        ret.exponente = f1.exponente;
        int maxPrecision; //dese donde restar
        if(f1.precision == f2.precision) {
            ret.precision = f1.precision;
            ret.mantisa = new byte[ret.precision];
            maxPrecision = f1.precision-1;
        } else {
            if(f1.precision < f2.precision) {
                ret.precision = f2.precision;
                ret.mantisa = new byte[ret.precision];
                maxPrecision = f2.precision-1;
            } else { //f2.presicion > f1.presicion
                ret.precision = f1.precision;
                ret.mantisa = new byte[ret.precision];
                maxPrecision = f1.precision-1;
            }
        }
        bool hayCarry = false;
        for(int i=maxPrecision;i>=0;i--) {
            cout << "F1["<<i<<"]" << (int)f1.mantisa[i] << " - " << "F2["<<i<<"]"<< (int)f2.mantisa[i] << ". C: "<< hayCarry << endl;
            byte aux = f1.mantisa[i] - f2.mantisa[i] - (hayCarry?1:0);
            hayCarry = aux<0;
            if(hayCarry) aux=aux+10;
            ret.mantisa[i] = aux;
        }
    } else {//los exponentes son diferentes
        if(f1.exponente < f2.exponente) {
            Float aux;
            aux = f1;
            long long int dif = f2.exponente - f1.exponente;
            long long int nuevaP = aux.precision + dif;
            aux.exponente = f2.exponente;
            byte *mantisaAux =  new byte[nuevaP];
            for(int i=0;i<dif;i++)
                mantisaAux[i] = 0;
            for(int i=dif;i<nuevaP;i++)
                mantisaAux[i] = aux.mantisa[i-dif];
            delete [] aux.mantisa;
            aux.mantisa = mantisaAux;
            aux.precision = nuevaP;
            aux.exponente = f2.exponente;
            Float::sumaDistitnoSigno(aux,f2,ret,cambiarSigno);
        } else {
            Float aux;
            aux = f2;
            long long int dif = f1.exponente - f2.exponente;
            long long int nuevaP = aux.precision + dif;
            aux.exponente = f1.exponente;
            byte *mantisaAux =  new byte[nuevaP];
            for(int i=0;i<dif;i++)
                mantisaAux[i] = 0;
            for(int i=dif;i<nuevaP;i++)
                mantisaAux[i] = aux.mantisa[i-dif];
            delete [] aux.mantisa;
            aux.mantisa = mantisaAux;
            aux.precision = nuevaP;
            aux.exponente = f1.exponente;
            Float::sumaDistitnoSigno(f1,aux,ret,cambiarSigno);
            while(ret.mantisa[ret.precision-1] == 0) {
		ret.precision--;
	    }
        }
    } 
    while(ret.mantisa[ret.precision-1] == 0 && ret.precision > 0 ){
            ret.precision--;
    }
    int nuevaPos=0;
    while(ret.mantisa[nuevaPos] == 0 && nuevaPos < ret.precision) {
    	nuevaPos++;
    }
    cout << "NUEVAPOST: " << nuevaPos << endl;
    int desde = 0;
//    while(nuevaPos<ret.precision) {
//	ret.mantisa[desde] = ret.mantisa[desde+nuevaPos];
//    	ret.mantisa[desde+nuevaPos] = 0;
//    	ret.precision--;
//    	ret.exponente--;
//    	desde++;
//    } 
    ret.signo = (f1.signo ^ cambiarSigno); //nxor
}

void Float::operator=(const Float& f) {
    signo = f.signo;
    exponente = f.exponente;
    precision = f.precision;
    if(!&mantisa == NULL)
        delete [] mantisa;
    mantisa = new byte[precision];
    for(int i=0;i<precision;i++)
        mantisa[i] = f.mantisa[i];
}


/** NI VAN EN EL TP CAPAZ **/
ostream& operator<<(ostream& out, const Float& f) {
    out << f.mostrar() << endl;
    return out;
}

void Float::cambiarSigno() {
	signo = !signo;
}

string Float::mostrar() const {
    string result = "";
    if(signo)
        cout << "-";
//        result += "-";
    cout << "0,";
    for(int i=0;i<precision;i++)
        cout << ((int)mantisa[i]);
//        result+= (mantisa[i]+'0');
    cout << " *10^ ";
//    result+= " *10^ ";
//    long long int ex = exponente;
    if(exponente < 0) {
//        result+="-";
//        ex*=(-1);

    }
    cout << exponente << endl;
//    result+= ex;
    return result;
}


bool Float::operator<= (const Float& f) const{
	if(exponente < f.exponente) {
		return true;
	} else if(exponente > f.exponente) {
		return false;
	} else {
		bool menorOIgual=true;
		int minPrecision = precision;
		if(f.precision < minPrecision) minPrecision = f.precision;
		for(int i=0;menorOIgual && i<minPrecision;i++)
			menorOIgual = mantisa[i] <= f.mantisa[i];
		if(!menorOIgual) {
			return false;
		} else {
			return precision <= f.precision;
		}
	}
}

#endif

